/*
 * Copyright (c) 2020-2021, SERI Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-05-22     Lyons        first version
 */

module pa_fpu_top (
    clk_i,
    rst_n_i,

    src0_i,
    src1_i,
    srci_i,

    single_i,
    double_i,

    sel_unit_i,
    sel_func_i,

    rm_i, //not use yet

    fresult_o,
    fresult_single_o,
    fresult_double_o,
    fresult_vld_o,

    iresult_o,
    iresult_vld_o
    );

`include "pa_fpu_param.v"

input                           clk_i;
input                           rst_n_i;

input                           src0_i;
input                           src1_i;
input                           srci_i;

input                           single_i;
input                           double_i;

input                           sel_unit_i;
input                           sel_func_i;

input                           rm_i;

output                          fresult_o;
output                          fresult_single_o;
output                          fresult_double_o;
output                          fresult_vld_o;

output                          iresult_o;
output                          iresult_vld_o;

wire                            clk_i;
wire                            rst_n_i;

wire [`FLOAT_WIDTH-1:0]         src0_i;
wire [`FLOAT_WIDTH-1:0]         src1_i;
wire [`INT_WIDTH-1:0]           srci_i;

wire                            single_i;
wire                            double_i;

wire [2:0]                      sel_unit_i;
wire [9:0]                      sel_func_i;

wire [2:0]                      rm_i;

wire [`FLOAT_WIDTH-1:0]         fresult_o;
wire                            fresult_single_o;
wire                            fresult_double_o;
wire                            fresult_vld_o;

wire [`INT_WIDTH-1:0]           iresult_o;
wire                            iresult_vld_o;


reg  [`FLOAT_WIDTH-1:0]         ex1_src0;
reg  [`FLOAT_WIDTH-1:0]         ex1_src1;
reg  [`FLOAT_WIDTH-1:0]         ex1_src2;
reg  [`INT_WIDTH-1:0]           ex1_srci;

reg                             ex1_single;
reg                             ex1_double;

reg  [2:0]                      ex1_sel_unit;
reg  [9:0]                      ex1_sel_func;

reg  [2:0]                      ex1_rm;

always @ (posedge clk_i) begin
    ex1_src0[`DOUBLE_WIDTH-1:0] <= src0_i[`DOUBLE_WIDTH-1:0];
    ex1_src1[`DOUBLE_WIDTH-1:0] <= src1_i[`DOUBLE_WIDTH-1:0];
    ex1_src2[`DOUBLE_WIDTH-1:0] <= 64'b0; //not use yet
    ex1_srci[`INT_WIDTH-1:0]    <= srci_i[`INT_WIDTH-1:0];

    ex1_single                  <= single_i;
    ex1_double                  <= double_i;

    ex1_sel_unit[2:0]           <= sel_unit_i[2:0];
    ex1_sel_func[9:0]           <= sel_func_i[9:0];

    ex1_rm[2:0]                 <= rm_i[2:0];
end

wire [`TYPE_WIDTH-1:0]          ex1_src0_type;
wire [`TYPE_WIDTH-1:0]          ex1_src1_type;
wire [`TYPE_WIDTH-1:0]          ex1_src2_type;

pa_fpu_data_judge u_pa_fpu_data_judge_src0 (
    .single_i                   (ex1_single),
    .double_i                   (ex1_double),
    .src_i                      (ex1_src0),
    .rst_type_o                 (ex1_src0_type)
);

pa_fpu_data_judge u_pa_fpu_data_judge_src1 (
    .single_i                   (ex1_single),
    .double_i                   (ex1_double),
    .src_i                      (ex1_src1),
    .rst_type_o                 (ex1_src1_type)
);

pa_fpu_data_judge u_pa_fpu_data_judge_src2 (
    .single_i                   (ex1_single),
    .double_i                   (ex1_double),
    .src_i                      (ex1_src2),
    .rst_type_o                 (ex1_src2_type)
);

wire [2:0]                      ex1_type_cnan;
wire [2:0]                      ex1_type_qnan;
wire [2:0]                      ex1_type_snan;
wire [2:0]                      ex1_type_inf;
wire [2:0]                      ex1_type_normal;
wire [2:0]                      ex1_type_subnormal;
wire [2:0]                      ex1_type_zero;

assign ex1_type_cnan[2:0]      = {ex1_src2_type[6], ex1_src1_type[6], ex1_src0_type[6]};
assign ex1_type_qnan[2:0]      = {ex1_src2_type[5], ex1_src1_type[5], ex1_src0_type[5]};
assign ex1_type_snan[2:0]      = {ex1_src2_type[4], ex1_src1_type[4], ex1_src0_type[4]};
assign ex1_type_inf[2:0]       = {ex1_src2_type[3], ex1_src1_type[3], ex1_src0_type[3]};
assign ex1_type_normal[2:0]    = {ex1_src2_type[2], ex1_src1_type[2], ex1_src0_type[2]};
assign ex1_type_subnormal[2:0] = {ex1_src2_type[1], ex1_src1_type[1], ex1_src0_type[1]};
assign ex1_type_zero[2:0]      = {ex1_src2_type[0], ex1_src1_type[0], ex1_src0_type[0]};

reg                             falu_sel_vld; //not change during proc

always @ (*) begin
case (ex1_sel_unit[2:0])
    `SEL_UNIT_FALU : begin
        falu_sel_vld <= 1'b1;
    end
    default : begin
        falu_sel_vld <= 1'b0;
    end
endcase
end

wire [`FLOAT_WIDTH-1:0]         ex2_falu_fresult;
wire                            ex2_falu_fresult_vld;

wire [`INT_WIDTH-1:0]           ex2_falu_iresult;
wire                            ex2_falu_iresult_vld;

wire [`FLOAT_WIDTH-1:0]         ex4_falu_fresult;
wire                            ex4_falu_fresult_vld;

wire [`INT_WIDTH-1:0]           ex4_falu_iresult;
wire                            ex4_falu_iresult_vld;

pa_fpu_falu_top u_pa_fpu_falu_top (
    .clk_i                      (clk_i),
    .rst_n_i                    (rst_n_i),

    .src0_i                     (ex1_src0),
    .src1_i                     (ex1_src1),
    .srci_i                     (ex1_srci),

    .single_i                   (ex1_single),
    .double_i                   (ex1_double),

    .sel_unit_i                 (ex1_sel_unit),
    .sel_func_i                 (ex1_sel_func),

    .type_cnan_i                (ex1_type_cnan),
    .type_qnan_i                (ex1_type_qnan),
    .type_snan_i                (ex1_type_snan),
    .type_inf_i                 (ex1_type_inf),
    .type_normal_i              (ex1_type_normal),
    .type_subnormal_i           (ex1_type_subnormal),
    .type_zero_i                (ex1_type_zero),

    .rm_i                       (ex1_rm),

    .ex1_clk_i                  (clk_i),
    .ex1_pipedown_i             (falu_sel_vld),
    .ex1_cancel_i               (1'b0),
    .ex2_clk_i                  (clk_i),
    .ex2_pipedown_i             (falu_sel_vld),
    .ex2_cancel_i               (1'b0),
    .ex3_clk_i                  (clk_i),
    .ex3_pipedown_i             (falu_sel_vld),
    .ex3_cancel_i               (1'b0),

    .ex2_fresult_o              (ex2_falu_fresult),
    .ex2_fresult_single_o       (),
    .ex2_fresult_double_o       (),
    .ex2_fresult_vld_o          (ex2_falu_fresult_vld),

    .ex2_iresult_o              (ex2_falu_iresult),
    .ex2_iresult_vld_o          (ex2_falu_iresult_vld),

    .ex4_fresult_o              (ex4_falu_fresult),
    .ex4_fresult_single_o       (),
    .ex4_fresult_double_o       (),
    .ex4_fresult_vld_o          (ex4_falu_fresult_vld),

    .ex4_iresult_o              (ex4_falu_iresult),
    .ex4_iresult_vld_o          (ex4_falu_iresult_vld)
);

wire [`FLOAT_WIDTH-1:0]         ex2_fresult;
wire [`FLOAT_WIDTH-1:0]         ex3_fresult;
wire [`FLOAT_WIDTH-1:0]         ex4_fresult;

wire [`INT_WIDTH-1:0]           ex2_iresult;

assign ex2_fresult[`FLOAT_WIDTH-1:0] = {{`FLOAT_WIDTH}{1'b0}};

assign ex3_fresult[`FLOAT_WIDTH-1:0] = {{`FLOAT_WIDTH}{1'b0}};

assign ex4_fresult[`FLOAT_WIDTH-1:0] = {{`FLOAT_WIDTH}{falu_sel_vld & ex4_falu_fresult_vld}} & ex4_falu_fresult[`FLOAT_WIDTH-1:0];

assign ex2_iresult[`INT_WIDTH-1:0]   = {{`INT_WIDTH  }{1'b0}};

assign fresult_o[`FLOAT_WIDTH-1:0] = ex2_fresult[`FLOAT_WIDTH-1:0]
                                   | ex3_fresult[`FLOAT_WIDTH-1:0]
                                   | ex4_fresult[`FLOAT_WIDTH-1:0];

assign iresult_o[`INT_WIDTH-1:0]   = ex2_iresult[`INT_WIDTH-1:0];

assign fresult_vld_o = falu_sel_vld & (1'b0 | ex4_falu_fresult_vld);

assign iresult_vld_o = falu_sel_vld & 1'b0;

endmodule
